/**
* Copyright 2014 SAP AG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.spotter.eclipse.ui.providers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.ui.navigator.CommonViewer;
import org.eclipse.ui.progress.UIJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spotter.eclipse.ui.Activator;
import org.spotter.eclipse.ui.ProjectNature;
import org.spotter.eclipse.ui.navigator.FixedOrderViewerComparator;
import org.spotter.eclipse.ui.navigator.ISpotterProjectElement;
import org.spotter.eclipse.ui.navigator.SpotterProjectParent;
import org.spotter.eclipse.ui.navigator.SpotterProjectResults;
import org.spotter.eclipse.ui.util.SpotterUtils;
/**
* Content provider for items of DynamicSpotter Project Navigator. This provider
* is used by the DynamicSpotter Project Navigator.
*
* @author Denis Knoepfle
*
*/
public class NavigatorContentProvider implements ITreeContentProvider, IResourceChangeListener {
private static final Logger LOGGER = LoggerFactory.getLogger(NavigatorContentProvider.class);
private static final Object[] NO_CHILDREN = {};
private final Map<String, Object> wrapperCache = new HashMap<String, Object>();
private CommonViewer viewer;
private boolean listenersRegistered;
private IDoubleClickListener dblClickOpenListener;
private ISelectionChangedListener projectSelectionListener;
/**
* Creates a new content provider. Registers the newly created instance as
* resource change listener at the workspace.
*/
public NavigatorContentProvider() {
ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
listenersRegistered = false;
Activator.getDefault().setNavigatorContentProvider(this);
}
@Override
public void dispose() {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
if (listenersRegistered && viewer != null) {
removeListeners();
}
Activator.getDefault().setNavigatorContentProvider(null);
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
if (!(viewer instanceof CommonViewer)) {
throw new IllegalStateException("Illegal viewer type provided");
}
if (listenersRegistered && this.viewer != null) {
removeListeners();
}
this.viewer = (CommonViewer) viewer;
this.viewer.setComparator(new FixedOrderViewerComparator());
registerListeners();
}
/**
* Returns the common viewer currently associated with this content
* provider.
*
* @return the currently associated viewer
*/
public CommonViewer getViewer() {
return this.viewer;
}
private void removeListeners() {
viewer.removeDoubleClickListener(dblClickOpenListener);
viewer.removeSelectionChangedListener(projectSelectionListener);
Activator.getDefault().setSelectedProjects(viewer, Collections.<IProject> emptySet());
dblClickOpenListener = null;
projectSelectionListener = null;
listenersRegistered = false;
}
private void registerListeners() {
dblClickOpenListener = createDblClickOpenListener();
projectSelectionListener = createProjectSelectionListener();
viewer.addDoubleClickListener(dblClickOpenListener);
viewer.addSelectionChangedListener(projectSelectionListener);
listenersRegistered = true;
}
private IDoubleClickListener createDblClickOpenListener() {
IDoubleClickListener listener = new IDoubleClickListener() {
@Override
public void doubleClick(DoubleClickEvent event) {
IStructuredSelection sel = (IStructuredSelection) event.getSelection();
SpotterUtils.openElement(sel.getFirstElement());
}
};
return listener;
}
private ISelectionChangedListener createProjectSelectionListener() {
ISelectionChangedListener listener = new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
IStructuredSelection sel = (IStructuredSelection) event.getSelection();
Set<IProject> selectedProjects = new HashSet<IProject>();
if (!sel.isEmpty()) {
for (Object obj : sel.toArray()) {
if (obj instanceof ISpotterProjectElement) {
ISpotterProjectElement element = (ISpotterProjectElement) obj;
selectedProjects.add(element.getProject());
if (!(obj instanceof SpotterProjectParent) && selectedProjects.size() >= 2) {
// already another project selected and mixed
// element types are not allowed
selectedProjects.clear();
break;
}
}
}
}
Activator.getDefault().setSelectedProjects(viewer, selectedProjects);
}
};
return listener;
}
/**
* Removes a project from the cache.
*
* @param projectName
* the name of the project to remove
*/
public void removeCachedProject(String projectName) {
wrapperCache.remove(projectName);
}
@Override
public Object[] getElements(Object inputElement) {
return getChildren(inputElement);
}
@Override
public Object[] getChildren(Object parentElement) {
Object[] children = null;
if (parentElement instanceof IWorkspaceRoot) {
IProject[] projects = ((IWorkspaceRoot) parentElement).getProjects();
children = createSpotterProjectParents(projects);
} else if (parentElement instanceof ISpotterProjectElement) {
children = ((ISpotterProjectElement) parentElement).getChildren();
} else {
children = NO_CHILDREN;
}
return children;
}
private Object[] createSpotterProjectParents(IProject[] projects) {
Object[] result = null;
List<Object> list = new ArrayList<>();
Set<String> deadProjects = new HashSet<>();
deadProjects.addAll(wrapperCache.keySet());
for (int i = 0; i < projects.length; i++) {
String projectName = projects[i].getName();
deadProjects.remove(projectName);
Object spotterProjectParent = wrapperCache.get(projectName);
if (spotterProjectParent == null) {
spotterProjectParent = createSpotterProjectParent(projects[i]);
wrapperCache.put(projects[i].getName(), spotterProjectParent);
}
if (spotterProjectParent != null) {
list.add(spotterProjectParent);
} // else ignore the project
}
// clean up the cache from projects that no longer exist
for (String deadProject : deadProjects) {
wrapperCache.remove(deadProject);
}
result = new Object[list.size()];
Map<String, SpotterProjectResults> projectHistoryElements = Activator.getDefault().getProjectHistoryElements();
projectHistoryElements.clear();
for (Object element : list) {
SpotterProjectParent projectParent = (SpotterProjectParent) element;
for (Object child : projectParent.getChildren()) {
if (child instanceof SpotterProjectResults) {
projectHistoryElements.put(projectParent.getProject().getName(), (SpotterProjectResults) child);
}
}
}
list.toArray(result);
return result;
}
private Object createSpotterProjectParent(IProject parentElement) {
Object result = null;
try {
if (parentElement.getNature(ProjectNature.NATURE_ID) != null) {
result = new SpotterProjectParent(parentElement);
}
} catch (CoreException e) {
LOGGER.warn("Unable to resolve nature for project " + parentElement.getName() + ".", e);
// Ignore and go to the next IProject
}
return result;
}
@Override
public Object getParent(Object element) {
Object parent = null;
if (IProject.class.isInstance(element)) {
parent = ((IProject) element).getWorkspace().getRoot();
} else if (ISpotterProjectElement.class.isInstance(element)) {
parent = ((ISpotterProjectElement) element).getParent();
}
return parent;
}
@Override
public boolean hasChildren(Object element) {
boolean hasChildren = false;
if (IWorkspaceRoot.class.isInstance(element)) {
hasChildren = ((IWorkspaceRoot) element).getProjects().length > 0;
} else if (ISpotterProjectElement.class.isInstance(element)) {
hasChildren = ((ISpotterProjectElement) element).hasChildren();
}
// else it is not one of these so return false
return hasChildren;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org
* .eclipse.core.resources.IResourceChangeEvent)
*/
@Override
public void resourceChanged(IResourceChangeEvent event) {
UIJob uiJob = new UIJob("refresh Project Navigator") { //$NON-NLS-1$
public IStatus runInUIThread(IProgressMonitor monitor) {
if (viewer != null && !viewer.getControl().isDisposed() && !viewer.isBusy()) {
TreePath[] treePaths = viewer.getExpandedTreePaths();
viewer.refresh();
viewer.setExpandedTreePaths(treePaths);
}
return Status.OK_STATUS;
}
};
uiJob.schedule();
}
}